package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.leanring.LearningRecordFormDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 * @author 陈彦祖
 * @since 2024-11-06
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;

    /**
     * 查询我的学习计划
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlan(PageQuery pageQuery) {
        LearningPlanPageVO vo = new LearningPlanPageVO();
        //1，获取当前登录人用户
        Long userId = UserContext.getUser();
        // 2，分页查询我的课表
        Page<LearningLesson> pageResult = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        //2.1，list集合装起来
        List<LearningLesson> records = pageResult.getRecords();
        //2.2,判断集合是否为空
        if (ObjectUtil.isEmpty(records)){
            return vo.emptyPage(pageResult);
        }
        //3，查询课程集合填充数据
        Map<Long , CourseSimpleInfoDTO> courseMap = getLongCourseSimpleInfoDTOMap(records);
        //4，统计学习表中本周学习的小节数量
        //select r.`lesson_id`as id,count(1) as num from learning_record r where r.`user_id` = 2 and r.`finished` = 1
        //and r.`finish_time` between '2024-11-04 00:00:00' and '2024-11-10 23:59:59'
        //group by r.`lesson_id`
        //4.1,获取本周开始时间和结束时间
        LocalDateTime startTime = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime endTime = DateUtils.getWeekEndTime(LocalDate.now());
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("`lesson_id`as id,count(1) as num")
                .eq("user_id",userId)
                .eq("finished",true)
                .between("finish_time",startTime,endTime)
                .groupBy("lesson_id");
        List<Map<String, Object>> mapList = recordMapper.selectMaps(queryWrapper);
        Map<Long, Long> learnedSectionNumMap = mapList.stream()
                .collect(Collectors.toMap(r -> (Long) r.get("id"), r -> (Long) r.get("num")));
        // 5、循环之前查询的分页课表数据，进行数据填充
        List<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            // 5.1、拷贝po至vo
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);

            // 5.2、填充课程数据
            CourseSimpleInfoDTO couseInfo = courseMap.get(record.getCourseId());
            if(ObjectUtil.isNotEmpty(couseInfo)){
                learningPlanVO.setCourseName(couseInfo.getName());
                learningPlanVO.setSections(couseInfo.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            learningPlanVO.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(record.getId(),0L).intValue());

            // 5.4、添加vo至list
            voList.add(learningPlanVO);
        }

        // 6、计算本周计划学习的总小节数
        //select sum(week_freq) from learning_lesson where user_id = 2 and plan_status = 1
        //and status in (0,1)
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", userId)
                .eq("plan_status", PlanStatus.PLAN_RUNNING)
                .in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .one();

        // 7、计算本周已学习的总小节数
        long sum = learnedSectionNumMap.values().stream().mapToLong(num -> num.longValue()).sum();
        Integer weekFinished = Long.valueOf(sum).intValue();

        /*long sum1 = learnedSectionNumMap.values().stream().flatMapToLong(num -> LongStream.of(num)).sum();
        Long sum2 = learnedSectionNumMap.values().stream().map(Function.identity()).reduce(Long::sum).get();
        Long sum3 = learnedSectionNumMap.values().stream().collect(Collectors.summingLong(num -> num));
        long sum4 = learnedSectionNumMap.values().stream().mapToLong(num -> num).summaryStatistics().getSum();*/

        // 8、封装Vo并返回
        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
        vo.setWeekFinished(weekFinished);
        vo.setWeekPoints(null); // TODO 本周学习积分
        return vo.pageInfo(pageResult.getTotal(),pageResult.getPages(),voList);
    }

    /**7
     * 创建修改学习计划
     * @param planDTO
     */
    @Override
    public void createPlans(LearningPlanDTO planDTO) {
        //1，查询课表信息course_id user_id
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), planDTO.getCourseId()));
        //2,更新学习计划数据
        // 2、更新学习计划数据
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN,LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId,lesson.getId())
                .update();
    }

    /**6
     * 统计课程学习人数
     * @param courseId
     * @return
     */
    @Override
    public Integer countStudent(Long courseId) {
        //select * from learning_lesson where course_id = #{courseId} and status in (0,1,2)
        // 1，统计
        return lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
    }

    /**5
     * 查询指定课程的学习记录
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLesson(Long courseId) {
        //1，获取当前登录人用户
        Long userId = UserContext.getUser();
        // 2，查询课程信息
        //select * from learning_lesson where user_id = #{userId} and course_id = #{courseId}
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        // 2，判断记录是否存在
        if (lesson == null){
            return null;
        }
        //3，处理vo
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    /**4
     * 检查课程是否有效
     * @param courseId
     * @return
     */
    @Override
    public Long checkCourseValid(Long courseId) {
        //1，获取当前用户
        Long userId = UserContext.getUser();
        if (userId == null){
            return null;
        }
        //2，查询课程信息
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        //3，判断课程是否存在
        if (lesson == null || LessonStatus.EXPIRED.equals(lesson.getStatus())){
            return null;
        }
        return lesson.getId();
    }

    /**3
     * 删除课程表
     * @param courseId
     */
    @Override
    public void deleteLesson(Long userId, Long courseId) {
        //1,获取当前登录用户判断是否是空
        if (userId == null){
            userId = UserContext.getUser();
        }
        //2，删除课程
        remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 常用条件封装
     * @param userId 用户id
     * @param courseId 课程id
     * @return
     */
    private Wrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }

    /**2
     * 查询正在学习的课程
     * @return
     */
    @Override
    public LearningLessonVO queryNowLearning() {
        //1,获取当前登录人id
        Long userId = UserContext.getUser();
        //2,查询正在学习的课程
        //select * from learning_lesson where user_id = #{userId} and status = 1
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                //3,根据创建时间倒序
                .orderByDesc(LearningLesson::getLatestLearnTime)
                //4,只查询一条记录
                .last("limit 1")
                .one();
        //5,判断记录是否存在
        if (lesson == null){
            return null;
        }
        //6,拷贝po属性到vo当中
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        //7，查询课程信息
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        //8，判断课程是否存在
        if (courseInfo == null){
            throw  new BadRequestException("课程不存在");
        }
        //9,拷贝设置set课程信息
        vo.setCourseName(courseInfo.getName());
        vo.setCourseCoverUrl(courseInfo.getCoverUrl());
        vo.setSections(courseInfo.getSectionNum());
        //10,统计课程中的课程数据
        Integer count = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(count);
        //11,查询课程章节信息
        List<CataSimpleInfoDTO> courseSimpleInfo = catalogueClient
                .batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        //12,判断章节信息是否存在
        if (!ObjectUtils.isAllEmpty(courseSimpleInfo)){
            CataSimpleInfoDTO cataSimpleInfoDTO = courseSimpleInfo.get(0);
            vo.setLatestSectionName(cataSimpleInfoDTO.getName());
            vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());
        }
        return vo;
    }

    /**
     * 添加课程
     * @param userId
     * @param courseIds
     */
    @Override
    public void addLesson(Long userId, List<Long> courseIds) {
        //1,根据id集合查询课程信息
        List<CourseSimpleInfoDTO> courseClientSimpleInfoList = courseClient.getSimpleInfoList(courseIds);
        //1.1,判断课程信息是否存在
        if(CollUtils.isEmpty(courseClientSimpleInfoList)){
            log.warn("课程信息不存在，课程id集合：{}",courseIds);
            return;
        }
        //2，循环组装课表数据信息
        List<LearningLesson> learningLessons = new ArrayList<>(courseClientSimpleInfoList.size());
        for (CourseSimpleInfoDTO courseSimpleInfo : courseClientSimpleInfoList) {
            LearningLesson learningLesson = new LearningLesson();
            //2.1，填充学员id，课程id
            learningLesson.setUserId(userId);
            learningLesson.setCourseId(courseSimpleInfo.getId());
            //2.2，填充课程信息
            Integer validDuration = courseSimpleInfo.getValidDuration();
            //2.3,判断课程过期时间
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0){
                continue;
            }
            //2.4，设置课程过期时间
            learningLesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            //2.5，添加到集合中
            learningLessons.add(learningLesson);
        }
        //3，批量添加
        saveBatch(learningLessons);
    }

    /**1
     * 分页查询我的课程表
     * @param pageQuery
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessonPage(PageQuery pageQuery) {
        //1，获取当前登录人id
        Long userId = UserContext.getUser();

        //2，分页查询
        //select * from learning_lesson where user_id = #{userId} order by lastest_learn_time desc limit 0,5
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        //3，获取记录
        List<LearningLesson> records = page.getRecords();
        if(ObjectUtil.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //4.获取课程id集合
        Map<Long, CourseSimpleInfoDTO> collectMap = getLongCourseSimpleInfoDTOMap(records);

        //5,循环分页结果，将po 转换成vo
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            //5.1,属性拷贝到vo
            LearningLessonVO v = BeanUtils.toBean(record, LearningLessonVO.class);
            CourseSimpleInfoDTO courseInfo = collectMap.get(record.getCourseId());
            v.setCourseName(courseInfo.getName());
            v.setCourseCoverUrl(courseInfo.getCoverUrl());
            v.setSections(courseInfo.getSectionNum());
            list.add(v);
        }
        return PageDTO.of(page, list);
    }

    /**
     * 获取课程信息
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> getLongCourseSimpleInfoDTOMap(List<LearningLesson> records) {
        //4，获取课程id集合
        Set<Long> collectIds = records.stream()
                .map(LearningLesson::getCourseId)
                .collect(Collectors.toSet());
        //4.1,发起远程调用
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(collectIds);
        //4.2，健壮性判断
        if (ObjectUtil.isEmpty(courseInfoList)){
            throw new BizIllegalException("课程信息不存在");
        }
        //4.3,将list转为map，key为课程id，value为课程信息
        Map<Long, CourseSimpleInfoDTO> collectMap = courseInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, dto -> dto));
        return collectMap;
    }
}
